//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include <drvpub.h>
#include <lcd.h>
#include "..\display\font.h"
#include "..\display\_display.h"
#include "gpostern.h"
#include <stdlib.h>
#include "wav.h"
#include <display.h>

extern UInt32 g_GState;
extern GPstMenuItem g_GPstMainMenuItem[MAX_POSTERN_CELL];
extern Position g_PosCursor;
extern GFuncCell g_EndProc;
EXTERN_C Display_Address g_dispAddr;

typedef struct tagRECT {
    Int32 left;
    Int32 top;
    Int32 right;
    Int32 bottom;
} RECT, *PRECT;


ECode GFunctest(void* argv)
{
    GDrawFrame();
    GPrintf("test in GFUCTEST\n");
    GPrintf("hello world");
    g_GState = FUNC;
    return NOERROR;
}

ECode GQuitSubFunc(void *argv)
{
    GInitMenuInfo(g_GPstMainMenuItem, MAX_POSTERN_CELL);
    GDrawFrame();
    GDrawMenuItem();
    g_GState = MENU0;
    return NOERROR;
}

//vibration test
ECode StartVibration(void *argv)
{
    ECode ec;
    IDeviceDriver *pAudio = NULL;
    ec = DzFindService(
            L"device:wave0", (PInterface *)&pAudio);
    if (FAILED(ec)) {
        //kprintf("GPostern: Can not find pAudio service!\n");
        return E_ACCESS_DENIED;
    }

    ec = pAudio->Control(0x22, NULL_MEMORYBUF, NULL, NULL);
    pAudio->Release();
    return NOERROR;
}


ECode StopVibration(void *argv)
{
    ECode ec;
    IDeviceDriver *pAudio = NULL;
    ec = DzFindService(
            L"device:wave0", (PInterface *)&pAudio);
    if (FAILED(ec)) {
        //kprintf("GPostern: Can not find pAudio service!\n");
        return E_ACCESS_DENIED;
    }

    ec = pAudio->Control(0x23, NULL_MEMORYBUF, NULL, NULL);
    pAudio->Release();
    return NOERROR;
}

GPstMenuItem Vibration[] =
{
    {StartVibration, "StartVibration", "StartVibration"},
    {StopVibration, "StopVibration", "StopVibration"},
    {GQuitSubFunc, "quit", "quit"}
};

ECode VibrationTest(void *argv)
{
    g_GState = MENU1;
    GInitMenuInfo(Vibration, 3);
    GDrawFrame();
    GDrawMenuItem();
    return NOERROR;
}

//LCD TEST
ECode BlackScreen(void *argv)
{
    g_GState = FUNC;
    Position pTLPoint;
    pTLPoint.x = 0;
    pTLPoint.y = 0;
    Position pRBPoint;
    pRBPoint.x = LCD_WIDTH;
    pRBPoint.y = LCD_HEIGHT;
    DrawRect(&pTLPoint, &pRBPoint, cblack);
    return NOERROR;
}

ECode WhiteScreen(void *argv)
{
    g_GState = FUNC;
    Position pTLPoint;
    pTLPoint.x = 0;
    pTLPoint.y = 0;
    Position pRBPoint;
    pRBPoint.x = LCD_WIDTH;
    pRBPoint.y = LCD_HEIGHT;
    DrawRect(&pTLPoint, &pRBPoint, cwhite);
    return NOERROR;
}

GPstMenuItem LCD[] =
{
    {BlackScreen, "BlackScreen", "BlackScreen"},
    {WhiteScreen, "WhiteScreen", "WhiteScreen"},
    {GQuitSubFunc, "quit", "quit"}
};

ECode LCDTest(void *argv)
{
    g_GState = MENU1;
    GInitMenuInfo(LCD, 3);
    GDrawFrame();
    GDrawMenuItem();
    return NOERROR;
}
//AudioCard test
INLINE ECode WaveOutControlSpeaker(IInterface  *HSound, bool bOpen)
{
    int nControlCode = bOpen ? 0x20 : 0x21;
    return ((IDeviceDriver*)HSound)->Control(nControlCode, NULL_MEMORYBUF,
        NULL, NULL);
}

INLINE ECode WaveOutInitial(IInterface  *HSound,UInt32 Rate, UInt16 BitsPerSample, UInt8 Channels)
{
    //kprintf("waveoutinitial \n");
    MemoryBuf_<8> buf;
    UInt64 arg=(UInt64)(Rate & 0xFFFFFFFF)
    | ((UInt64)(BitsPerSample & 0xFFFF)<< 32)
    | ((UInt64)(Channels & 0xFF) << 48);
    buf.Copy((Byte *)&arg,sizeof(arg));
    return ((IDeviceDriver*)HSound)->Control(0x00,buf,&buf,NULL);
}

INLINE ECode  WaveOutSetVolume(IInterface  *HSound,UInt16 volume)
{
    MemoryBuf_<16> buf;
    buf.Copy((Byte *)&volume, sizeof(volume));
    return ((IDeviceDriver*)HSound)->Control(0x02,buf,&buf,NULL);
}

INLINE ECode WaveOutReset(IInterface  *HSound)
{
    return ((IDeviceDriver*)HSound)->Control(0x11,NULL_MEMORYBUF,NULL,NULL);
}

INLINE ECode WaveOutWrite(IInterface  *HSound,byte *pBuffer, UInt32 Size,UInt32 *pSizeWritten)
{
    MemoryBuf buf(pBuffer,Size,Size);
    return ((IDeviceDriver*)HSound)->Write(0,buf,(Int32 *)pSizeWritten,NULL);
}

INLINE ECode WaveOutClose(IInterface  *HSound)
{
    return ((IDeviceDriver*)HSound)->Control(0x10,NULL_MEMORYBUF,NULL,NULL);
}

/*chunkInfo: use to test the file's format*/
UInt32 chunkInfo(Byte *pBuffer, UInt32 size, char *dst, UInt32 *pAddr)
{
    Byte *p = pBuffer;
    UInt32 i = 0;
    while (i < size) {
        if (memcmp(p, dst, strlen(dst)) == 0) {
            *pAddr = i;
            return 1;
        }
        i++;
        p++;
    }
    return 0;
}

typedef struct wavFile {
    Byte *fh;
    UInt32 Sum;
    UInt32 Count;
} WavFile;
static WavFile g_WavFile;

INLINE void GWavFileOpen (Byte *fh, unsigned int Max)
{
    g_WavFile.fh = fh;
    g_WavFile.Sum = Max;
    g_WavFile.Count = 0;
}

int GWavRead (void* buffer, unsigned int count)
{

    if(g_WavFile.Count >= g_WavFile.Sum){
        return 0;
    }
    UInt32 ret = count;
    if((g_WavFile.Count+count) > g_WavFile.Sum){
        ret = g_WavFile.Sum - g_WavFile.Count;
    }
    memcpy(buffer, (void *)&g_WavFile.fh[g_WavFile.Count], ret);
    g_WavFile.Count += ret;
    return ret;
}

INLINE int GWavReadSet (unsigned int count)
{
    if(count > g_WavFile.Sum)
        return -1;
    g_WavFile.Count = count;
    return 0;
}


UInt32 ReadWaveFileHead(IInterface  *Sound)
{
    Byte buf[0x40];
    UInt32 bytesread, k;
    UInt32 Head = 0x40;
    UInt32 pAddr = 0;
    UInt32 addr;


    /*read in input*/
    /*test whether the file exists*/
    GWavReadSet (0);
    if (GWavRead(buf, Head) <= 0) {
        //kprintf("Problem reading file can not run.\n");
        return 0;
    }
    /*test whether the file is of RIFF and of WAVE or not*/
    if (!chunkInfo(buf, 64, "RIFF", &pAddr)\
            && !chunkInfo(buf, 64, "WAVE", &pAddr)) {
    	//kprintf("The file is not a RIFF or WAVE file.\n");
        return 0;
    }

    /*work out the Sample rate & Channels*/
    UInt8 channels = buf[0x16];
    UInt32 sampleRate = *((UInt32 *)&buf[0x18]);
    UInt16 BitsPerSample = buf[0x22];
    ECode ec;

    ec = WaveOutInitial(Sound, sampleRate, BitsPerSample, channels);
    ec = WaveOutReset(Sound);

    /*set the Master Volume*/
    UInt32 vol = 0xC0C0;

    ec = WaveOutSetVolume(Sound, vol);

    /*work out the size of the sound data*/
    if (!chunkInfo(buf, 64, "data", &pAddr)) {//look for the size of data
        return 0;
    }
    addr = pAddr + 4;
    bytesread = 0;
    k = (UInt32)buf[addr + 0x3];
    k = k << 24;
    bytesread = k;
    k = (UInt32)buf[addr + 0x2];
    k = k << 16;
    bytesread |= k;
    k = (UInt32)buf[addr + 0x1];
    k = k << 8;
    bytesread |= k;
    k = (UInt32)buf[addr];
    bytesread |= k;

    return bytesread;
}

#define PLAYBUF_SIZE 0x400

void PlayWaveFile(IInterface  *Sound, UInt32 BytesRead)
{
    UInt32 BytesWrite = 0;
    Byte *pWriteBuf;
    UInt32 nActualWrite, nNeedWrite;
    ECode ec;
    UInt32 count = 0;
    Byte RD_BUFFER[PLAYBUF_SIZE];

//    kprintf("Start playing...\n");
    /*write out*/
    while(count < 5){
        while (BytesWrite < BytesRead){
            nNeedWrite = GWavRead(RD_BUFFER, PLAYBUF_SIZE);
            if((int)nNeedWrite <= 0) {
                break;
            }
            pWriteBuf = RD_BUFFER;

            while (nNeedWrite > 0) {
                ec = WaveOutWrite(Sound, pWriteBuf, nNeedWrite, &nActualWrite);
                if (FAILED(ec)) {
//                   kprintf("Write error. ec = 0x%x\n", ec);
                }
                nNeedWrite -= nActualWrite;
                pWriteBuf += nActualWrite;
                BytesWrite += nActualWrite;
            }
            kprintf(".");
        }
        count++;
        GWavReadSet (0x40);
        BytesWrite = 0;
    }


//    _CThread_Sleep(1000 * 10000);

    WaveOutClose(Sound);
//    kprintf("\nFinshed playing...\n");
}

//Aodio test
ECode AodioPlayTest(void *argv)
{
    ECode ec;
    IInterface *Sound = NULL;
    Byte *fh = wav;
    ec = DzFindService(
            L"device:wave0", &Sound);
    if (FAILED(ec)) {
//        kprintf("GPostern: Can not find Sound service!\n");
        return E_ACCESS_DENIED;
    }

    WaveOutControlSpeaker(Sound, TRUE); // Open the speaker
    GWavFileOpen (fh, MAX_WAV);
    UInt32 FileByte = ReadWaveFileHead(Sound);
    /*read the file*/
    if (0 == FileByte) {
    	Sound->Release();
        return E_ACCESS_DENIED;
    }
    kprintf("the total file size is %d\n",FileByte);

    PlayWaveFile(Sound, FileByte);

    WaveOutControlSpeaker(Sound, FALSE); // Open the speaker

    if(NULL != Sound) {
        Sound->Release();
    }

    return NOERROR;
}

//Camera test

OverlayFormat g_Overlay2Fmt;
IDeviceDriver *g_pDisplay = NULL;
IDeviceDriver *g_pCamera = NULL;
ECode SetOverlay2(OverlayFormat *pOverlayFormat);

Boolean  InitFrameBuf(int *pnScreenWidth, int *pnScreenHeight)
{

    MemoryBuf_<32>      ezbModeName;
    MemoryBuf_<32>      ezbOutMode;
    MemoryBuf_<32>      ezbOutAddr;
    MemoryBuf_<sizeof(FrameBufferFormat)>      ezbOutFbFmt;
    FrameBufferFormat *pFbFmt;

    // Get the detail information of baseplane
    g_pDisplay->Control(7, NULL_MEMORYBUF, &ezbOutFbFmt, NULL);
    pFbFmt = (FrameBufferFormat *)ezbOutFbFmt.GetPayload();

    if (pFbFmt->Color != COLOR_RGBT666) {
        kprintf("This sample must run with color format RGBT666!\n");
        return FALSE;
    }

    *pnScreenWidth = pFbFmt->Width;
    *pnScreenHeight = pFbFmt->Height;

    return TRUE;
}

void FrameBufferSet(UInt32 *pBuf, UInt32 Color, UInt32 Size)
{
    UInt32 i = 0;

    while (i++ < Size) {
        *pBuf++ = Color;
    }
}

void ClearOverlay2()
{
    memset(g_Overlay2Fmt.pChannelFb[0], 0xFF, g_Overlay2Fmt.ChannelSize[0]);
    memset(g_Overlay2Fmt.pChannelFb[1], 0x80, g_Overlay2Fmt.ChannelSize[1]);
    memset(g_Overlay2Fmt.pChannelFb[2], 0x80, g_Overlay2Fmt.ChannelSize[2]);
}


Boolean GConfigureDisplay(RECT *CamRect)
{

    int nLCDScreenWidth;
    int nLCDScreenHeight;

    InitFrameBuf(&nLCDScreenWidth, &nLCDScreenHeight);
    CamRect->left = 0;
    CamRect->top = 0;
    CamRect->right = nLCDScreenWidth - 1;
    CamRect->bottom = nLCDScreenHeight - 1;

    // Clear base plane
    FrameBufferSet((UInt32 *)g_dispAddr.vaBasePlane, 0, nLCDScreenWidth * nLCDScreenHeight);

    // Set the preview window transparency in base plane.
    FrameBufferSet((UInt32 *)g_dispAddr.vaBasePlane,
        0x00040000, nLCDScreenWidth * nLCDScreenHeight);



    g_Overlay2Fmt.Enable = TRUE;
    g_Overlay2Fmt.Color = COLOR_YCbCr422; // MUST
    g_Overlay2Fmt.Layer = 2; // Use overlay2
    g_Overlay2Fmt.Bpp = 16; // YCrCb422
    g_Overlay2Fmt.Width = nLCDScreenWidth;
    g_Overlay2Fmt.Height = nLCDScreenHeight;

    g_Overlay2Fmt.PosX = CamRect->left;
    g_Overlay2Fmt.PosY = CamRect->top;

    SetOverlay2(&g_Overlay2Fmt);

    assert(g_Overlay2Fmt.Width == nLCDScreenWidth);

    // Get the whole information about overlay2

    g_Overlay2Fmt.pChannelFb[0] = (void *)g_dispAddr.paOverlay2Y;
    g_Overlay2Fmt.pChannelFb[1] = (void *)g_dispAddr.paOverlay2Cb;
    g_Overlay2Fmt.pChannelFb[2] = (void *)g_dispAddr.paOverlay2Cr;


    //clear overlay2
    ClearOverlay2();
    return TRUE;

}

Boolean GConfigureCamera()
{
    ECode ec;
    // Initialize camera device
    ec = g_pCamera->Control(0, NULL_MEMORYBUF, NULL, NULL);

    if (FAILED(ec)) {
        kprintf("ERROR: Can't init camera, ec = 0x%08x\n", ec);
        return FALSE;
    }

    return TRUE;
}

void GStartPreview(RECT *CamRect)
{
    MemoryBuf_<sizeof(RECT)>  ezbInRect;
    RECT *pRect = (RECT *)ezbInRect.GetPayload();
    pRect->left = CamRect->left;
    pRect->top = CamRect->top;
    pRect->right = CamRect->right;
    pRect->bottom = CamRect->bottom;
    ezbInRect.SetUsed(sizeof(RECT));

    g_pCamera->Control(3, ezbInRect, NULL, NULL);
}

ECode QuitCamera(void *arg)
{
    // Stop preview
    g_pCamera->Control(4, NULL_MEMORYBUF, NULL, NULL);
    ClearOverlay2();
    g_pDisplay->Control(10, NULL_MEMORYBUF, NULL, NULL);

    if (g_pDisplay) g_pDisplay->Release();

    if (g_pCamera) {
        // Quit camera device
        g_pCamera->Control(1, NULL_MEMORYBUF, NULL, NULL);
        g_pCamera->Release();
    }

    return NOERROR;
}

ECode CameraTest(void *argv)
{

    ECode ec;
    g_GState = FUNC;
    RECT CamRect;
    ec = DzFindService(
            L"device:camera0", (IInterface **)&g_pCamera);
    if (FAILED(ec)) {
        kprintf("ERROR: Can't find Service: camera, ec = 0x%08x\n", ec);
        return ec;
    }

    ec = DzFindService(
            L"device:display0", (IInterface **)&g_pDisplay);
    if (FAILED(ec)) {
        kprintf("ERROR: Can't find Service: display, ec = 0x%08x\n", ec);
        return ec;
    }


    if (!GConfigureDisplay(&CamRect)){
        return E_FAIL;
    }

    if (!GConfigureCamera()){
        return E_FAIL;
    }

    GStartPreview(&CamRect);

    g_EndProc = QuitCamera;
    return NOERROR;
}
